"""
Copyright (c) 2022 Huawei Technologies Co.,Ltd.

openGauss is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:

          http://license.coscl.org.cn/MulanPSL2

THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
"""
"""
Case Type   : 硬件故障类--磁盘
Case Name   : 审计日志超大导致磁盘空间占满
Create At   : 2020-10-15
Owner       : n@ningyali
Description :
    1、打开DML审计项开关，将audit_space_limit设置为1TB
    2、重复执行DML操作，查看审计日志大小
Expect      :
    1、设置成功
    2、反复执行DML操作，审计日志空间占用达到设置大小
    3、审计日志大小占满磁盘后数据库无法写入
    4、清楚故障后，数据库可写入
History     :
"""

import unittest

from yat.test import Node
from yat.test import macro

from testcase.utils.Common import *
from testcase.utils.CommonSH import *
from testcase.utils.Logger import *


class BigAuditFileAndDiskFull(unittest.TestCase):
    primary_sh = CommonSH('PrimaryDbUser')
    standby1_sh = CommonSH('Standby1DbUser')
    nodes_tuple = ('PrimaryDbUser', 'Standby1DbUser', 'Standby2DbUser')

    @RestartDbCluster(*nodes_tuple)
    def setUp(self):
        self.log = Logger()
        self.log.info('---------------Opengauss_Reliability_Hardware_Case009:开始执行-------------')
        self.primary_root_node = Node(node='PrimaryRoot')
        self.primary_user_node = Node(node='PrimaryDbUser')
        self.standby1_root_node = Node(node='Standby1Root')
        self.standby1_user_node = Node(node='Standby1DbUser')
        self.com = Common()
        self.Constant = Constant()
        self.sqlfile = 'reliability_hardware009.sql'
        self.t_name = 't_reliability_hardware009'

        self.log.info("-----------获取主节点数据目录所在磁盘 -----------")
        cmd = f"df -h {macro.DB_INSTANCE_PATH}"
        self.log.info(cmd)
        msg = self.primary_root_node.sh(cmd).result()
        self.log.info(msg)
        self.db_disk_name = msg.splitlines()[-1].split()[0].strip()

    def test_main(self):
        self.log.info("-------------打开DML审计项开关，将audit_space_limit设置为1T---------------")
        # 控制审计进程的开启和关闭。审计进程开启后，将从管道读取后台进程写入的审计信息，并写入审计文件。
        msg = self.primary_sh.executeGsguc('reload', self.Constant.GSGUC_SUCCESS_MSG, 'audit_enabled=on')
        self.assertTrue(msg)
        # 控制审计日志的保存策略，以空间还是时间限制为优先策略。•on表示采用空间优先策略，最多存储audit_space_limit大小的日志。•off表示采用时间优先策略。最少存储audit_file_remain_time长度时间的日志。
        msg = self.primary_sh.executeGsguc('reload', self.Constant.GSGUC_SUCCESS_MSG, 'audit_resource_policy=on')
        self.assertTrue(msg)
        # 审计文件占用的磁盘空间总量。
        msg = self.primary_sh.executeGsguc('reload', self.Constant.GSGUC_SUCCESS_MSG, f'audit_space_limit=1024GB')
        self.assertTrue(msg)
        # 默认值为12295，表示只对DATABASE、SCHEMA、USER、DATA SOURCE这四类数据库对象的CREATE、ALTER、DROP操作进行审计
        msg = self.primary_sh.executeGsguc('reload', self.Constant.GSGUC_SUCCESS_MSG, f'audit_system_object=524287')
        self.assertTrue(msg)
        # 决定是否对具体表的INSERT、UPDATE、DELETE操作进行审计
        msg = self.primary_sh.executeGsguc('reload', self.Constant.GSGUC_SUCCESS_MSG, f'audit_dml_state=1')
        self.assertTrue(msg)

        # 获取审计目录
        sql_cmd = "show audit_directory;"
        self.log.info(sql_cmd)
        msg = self.primary_sh.executDbSql(sql_cmd)
        self.log.info(msg)
        audit_directory = msg.splitlines()[-2].strip()

        self.log.info("--------------从yat主机拷贝文件到数据库主机---------------")
        # 目标主机新建路径
        self.to_path = os.path.dirname(macro.DB_INSTANCE_PATH) + '/test_script/'
        # 目标主机文件路径
        self.newsql_path = os.path.join(self.to_path, self.sqlfile)
        # 拷贝文件
        self.com.scp_file(self.primary_root_node, self.sqlfile, self.to_path)
        filesql_cmd = f"source {macro.DB_ENV_PATH};" \
            f"gsql -d {self.primary_user_node.db_name} -p {self.primary_user_node.db_port} -r -v t_name='{self.t_name}' -f {self.newsql_path}"
        self.log.info(filesql_cmd)

        self.log.info("-------------------注入磁盘满故障--------------------")
        cfe_command = f'rfile_full (diskname) values ({self.db_disk_name})'
        cfe_msg = self.com.cfe_inject(self.primary_root_node, cfe_command)
        self.log.info(cfe_msg)
        self.assertIn(self.Constant.CFE_DISK_FULL_SUCCESS_MSG, cfe_msg)

        for i in range(1000000):
            self.log.info("----------------查看审计日志大小-----------------")
            shell_cmd = f"du -sh {audit_directory}"
            self.log.info(shell_cmd)
            msg = self.primary_user_node.sh(shell_cmd).result()
            self.log.info(msg)

            self.log.info("----------------查看磁盘剩余可用空间-----------------")
            avail_size = self.com.getAvailSize(self.primary_root_node, macro.DB_INSTANCE_PATH)
            self.log.info('Available:' + str(avail_size))

            self.log.info(f"----------------第{i}次：重复执行DML操作-------------------")
            dml_result = self.primary_user_node.sh(filesql_cmd).result()
            self.log.info('sqlexecute result: ' + dml_result)

            if self.Constant.DISK_FULL_MSG in dml_result or avail_size == '0':
                self.log.info('磁盘已满')
                break

        self.log.info("----------------查看数据库状态-------------------")
        self.log.info(self.standby1_sh.getDbClusterStatus('detail'))

        self.log.info("--------------审计日志大小占满磁盘后数据库无法写入--------------")
        msg = self.primary_user_node.sh(filesql_cmd).result()
        self.log.info('sqlexecute result: ' + msg)
        # 磁盘满可能导致数据库主节点stop，继而连接数据库失败
        if self.Constant.FAILED_CONNECT_DB not in msg:
            self.assertIn(self.Constant.DISK_FULL_MSG, msg)

        self.log.info("--------------删除审计日志以清除故障--------------")
        shell_cmd = f"for i in `ls {audit_directory}/*_adt`;do >$i; done"
        self.log.info(shell_cmd)
        msg = self.primary_user_node.sh(shell_cmd).result()
        self.log.info(msg)

        self.log.info("----------------查看磁盘剩余可用空间-----------------")
        avail_size = self.com.getAvailSize(self.primary_root_node, macro.DB_INSTANCE_PATH)
        self.log.info('Available:' + avail_size)

        self.log.info("----------------查看审计日志大小-----------------")
        shell_cmd = f"du -sh {audit_directory}"
        self.log.info(shell_cmd)
        msg = self.primary_user_node.sh(shell_cmd).result()
        msg = msg.split()[0].strip()
        self.log.info(msg)

        self.log.info("----------------------------清除故障-----------------------------")
        cfe_command = f"rfile_full where (diskname={self.db_disk_name})"
        cfe_msg = self.com.cfe_clean(self.primary_root_node, cfe_command)
        self.log.info(cfe_msg)
        self.assertIn(self.Constant.CFE_DISK_CLEAN_SUCCESS_MSG, cfe_msg)

        self.log.info("----------------查看磁盘剩余可用空间-----------------")
        avail_size = self.com.getAvailSize(self.primary_root_node, macro.DB_INSTANCE_PATH)
        self.log.info('Available:' + avail_size)

        self.log.info("----------------磁盘满可能导致数据库主节点stop，重启集群以恢复-----------------")
        is_started = self.primary_sh.startDbCluster()
        self.log.info(is_started)

        self.assertTrue(self.primary_sh.getDbClusterStatus('status'))

        self.log.info("--------------清除故障后数据库可写入--------------")
        msg = self.primary_user_node.sh(filesql_cmd).result()
        self.log.info('sqlexecute result: ' + msg)
        for error_msg in self.Constant.SQL_WRONG_MSG:
            self.assertNotIn(error_msg, msg)

    def tearDown(self):
        self.log.info("----------------------------清除故障-----------------------------")
        cfe_command = f"rfile_full where (diskname={self.db_disk_name})"
        cfe_msg = self.com.cfe_clean(self.primary_root_node, cfe_command)
        self.log.info(cfe_msg)
        self.assertIn(self.Constant.CFE_DISK_CLEAN_SUCCESS_MSG, cfe_msg)

        self.log.info("--------------删除表--------------")
        sql_cmd = f"drop table if exists {self.t_name}"
        self.log.info(sql_cmd)
        msg = self.primary_sh.executDbSql(sql_cmd)
        self.log.info(msg)

        self.log.info("-----------删除拷贝的sql文件-----------")
        shell_cmd = f"rm -rf {self.newsql_path}; ls -l {self.newsql_path}"
        self.log.info(shell_cmd)
        msg = self.primary_root_node.sh(shell_cmd).result()
        self.log.info(msg)

        self.log.info("----------------查看磁盘剩余可用空间-----------------")
        avail_size = self.com.getAvailSize(self.primary_root_node, macro.DB_INSTANCE_PATH)
        self.log.info('Available:' + avail_size)

        self.log.info("-------------还原默认值---------------")
        # audit_enabled默认值为on，无需还原; audit_resource_policy默认值为on，无需还原
        # audit_space_limit默认值为1GB，需还原
        self.primary_sh.executeGsguc('reload', self.Constant.GSGUC_SUCCESS_MSG, f'audit_space_limit=1GB')
        self.primary_sh.executeGsguc('reload', self.Constant.GSGUC_SUCCESS_MSG, f'audit_system_object=12295')
        self.primary_sh.executeGsguc('reload', self.Constant.GSGUC_SUCCESS_MSG, f'audit_dml_state=0')

        self.log.info('-------------Opengauss_Reliability_Hardware_Case009:执行完成-------------')
